package com.chao.gateway.dynamicRoute;

import cn.hutool.json.JSONUtil;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executor;

/**
 * 
 * @Description:   
 * @className: DynamicRouteConfig 
 * @author fu wenchao
 * @date 2022/1/19 11:12 
 * @company:西安博达软件股份有限公司
 * @copyright: Copyright (c) 2022
 * @version V1.0 
 */
@Configuration
@Slf4j
public class NacosDynamicRouteConfig implements ApplicationEventPublisherAware {

    @Value("${nacos.gateway.route.config.data-id:gateway-route}")
    private String dataId;

    @Value("${nacos.gateway.route.config.group:GATEWAY_GROUP}")
    private String groupId;

    @Autowired
    private NacosDiscoveryProperties nacosDiscoveryProperties;

    @Autowired
    private RouteDefinitionWriter routedefinitionWriter;

    private ApplicationEventPublisher publisher;

    private static final List<String> ROUTE_ID_LIST = new ArrayList<>();

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }

    @PostConstruct
    public void initDynamicRouteByNacosListener(){
        try {
            Properties properties = new Properties();
            properties.put("serverAddr",nacosDiscoveryProperties.getServerAddr());
            properties.put("namespace",nacosDiscoveryProperties.getNamespace());
            //实例化配置连接
            ConfigService configService = NacosFactory.createConfigService(properties);
            log.info("dataId:{}",dataId);
            String config = configService.getConfig(dataId, groupId, 30000L);
            log.info("获取当前网关的配置：{}",JSONUtil.toJsonStr(config));
            //发布
            publishRoute(config);
            configService.addListener(dataId, groupId, new Listener() {
                @Override
                public Executor getExecutor() {
                    return null;
                }

                @Override
                public void receiveConfigInfo(String config) {
                    publishRoute(config);
                }
            });
        } catch (NacosException e) {
            e.printStackTrace();
        }
        log.info("路由id:{}",JSONUtil.toJsonStr(ROUTE_ID_LIST));
    }

    /**
     * @description 发布路由
     * @author fu wenchao
     * @date 2022/1/19 11:35
     * @param config 路由信息
     * @return void
     */
    private void publishRoute(String config){
        clearRoute();
        try {
            log.info("更新动态路由,内容：{}",JSONUtil.toJsonStr(config));
            List<RouteDefinition> routeDefinitions = JSONObject.parseArray(config, RouteDefinition.class);
            routeDefinitions.forEach(this::addRoute);
            publisher.publishEvent(new RefreshRoutesEvent(this.routedefinitionWriter));
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * @description 删除路由
     * @author fu wenchao
     * @date 2022/1/19 11:40
     * @param
     * @return void
     */
    private void clearRoute(){
        log.info("删除路由");
        ROUTE_ID_LIST.forEach(routerId -> {
            routedefinitionWriter.delete(Mono.just(routerId)).subscribe();
        });
        ROUTE_ID_LIST.clear();
    }

    private void addRoute(RouteDefinition routeDefinition){
        log.info("添加路由：{}",JSONUtil.toJsonStr(routeDefinition));
        try {
            routedefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
            ROUTE_ID_LIST.add(routeDefinition.getId());
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
